package aggregate

import (
	"fmt"
	"gluenet/internal/glue-srvp/domain/dependency"
	"gluenet/internal/glue-srvp/domain/entity"
	"gluenet/internal/glue-srvp/domain/vo"
	"gluenet/internal/pkg/infra"
	"gluenet/internal/pkg/infra/prepare"
)

func init() {
	prepare.RegisterFactory(func() {
		tagMetaFactory := NewTagCreateFactory()
		prepare.InjectDependency(func() *TagFactory {
			return tagMetaFactory
		})
	})
}

type TagFactory struct {
	MetaRepo    dependency.MetaRepo
	TagMetaRepo dependency.TagMetaRepo
}

func NewTagCreateFactory() *TagFactory {
	factory := &TagFactory{}
	prepare.FetchDependency(&factory.MetaRepo)
	prepare.FetchDependency(&factory.TagMetaRepo)
	return factory
}

func (factory *TagFactory) FindTag(auth infra.AuthInfo, query interface{}) ([]entity.Tag, error) {
	var obj []entity.Tag
	if err := factory.TagMetaRepo.FindTag(&obj, auth, query); err != nil {
		return nil, err
	}

	return obj, nil
}

func (factory *TagFactory) FindTagDetail(auth infra.AuthInfo, query map[string]interface{}) ([]entity.TagDetail, error) {
	var obj []entity.TagDetail

	query2 := make(map[string]interface{}, len(query))
	for k, v := range query {
		query2["label_tag_snap."+k] = v
	}
	if err := factory.TagMetaRepo.FindTagDetail(&obj, auth, query2); err != nil {
		return nil, err
	}

	return obj, nil
}

//func (factory *TagFactory) FindGroupSnap(query map[string]interface{}) ([]entity.TagGroupSnap, error) {
//	var obj []entity.TagGroupSnap
//	if err := factory.MetaRepo.FindBy(&obj, query); err != nil {
//		return nil, err
//	}
//
//	return obj, nil
//}

func (factory *TagFactory) FindGroup(auth infra.AuthInfo, query map[string]interface{}) ([]entity.TagGroup, error) {
	var obj []entity.TagGroup

	query2 := make(map[string]interface{}, len(query))
	for k, v := range query {
		query2["label_tag_group."+k] = v
	}
	if err := factory.TagMetaRepo.FindGroup(&obj, auth, query2); err != nil {
		return nil, err
	}
	return obj, nil
}

func (factory *TagFactory) CreateTag(auth infra.AuthInfo, req vo.CreateTagReq) (*entity.TagDetail, error) {
	var obj entity.Tag

	obj.Group.Value = req.GroupValue
	obj.Group.Describe = req.GroupDescribe
	obj.Value = req.TagValue
	obj.Describe = req.TagDescribe
	obj.Group.Id = req.GroupID

	// tag value 去重
	//var tagds []entity.Tag
	//if err := factory.TagMetaRepo.FindBy(&tagds, map[string]interface{}{
	//	"value": req.TagValue,
	//}); err != nil {
	//	return nil, err
	//}
	//if len(tagds) > 0 {
	//	return nil, fmt.Errorf("create tag failed, value is exist")
	//}

	if req.GroupID != 0 {
		var grp entity.TagGroup
		if err := factory.TagMetaRepo.FindOne(&grp, map[string]interface{}{
			"id": req.GroupID,
		}); err != nil {
			return nil, err
		}
		obj.Group.Value = grp.Value
		obj.Group.Describe = grp.Describe

		//} else {
		//var taggds []entity.TagGroup
		//if err := factory.TagMetaRepo.FindBy(&taggds, map[string]interface{}{
		//	"value": req.GroupValue,
		//}); err != nil {
		//	return nil, err
		//}
		//if len(taggds) > 0 {
		//	return nil, fmt.Errorf("create tag group failed, tag group value is exist")
		//}
	}

	if err := factory.TagMetaRepo.AddTag(&obj, auth); err != nil {
		return nil, err
	}

	var detail entity.TagDetail
	detail.Id = obj.Id
	detail.Value = obj.Value
	detail.Describe = obj.Describe
	detail.GroupID = obj.Group.Id
	detail.GroupValue = obj.Group.Value
	detail.GroupDescribe = obj.Group.Describe

	//
	//
	//var tagObj entity.Tag
	//tagObj.Value = req.TagValue
	//tagObj.Describe = req.TagDescribe
	//if len(req.GroupID) != 0 {
	//	groupID, _ := strconv.ParseInt(req.GroupID, 10, 64)
	//	tagObj.GroupID = groupID
	//} else {
	//	var tagGrpObj entity.TagGroup
	//	tagGrpObj.Value = req.GroupValue
	//	tagGrpObj.Describe = req.GroupDescribe
	//
	//	tagGrp, err := factory.MetaRepo.Add(&tagGrpObj)
	//	if err != nil {
	//		return nil, err
	//	}
	//	tagGrpE := tagGrp.(*entity.TagGroup)
	//
	//	var tagGrpUsrObj entity.TagGroupUser
	//	tagObj.GroupID = tagGrpE.Id
	//	tagGrpUsrObj.ID = tagGrpE.Id
	//	tagGrpUsrObj.UserID = user
	//	_, err = factory.MetaRepo.Add(&tagGrpUsrObj)
	//	if err != nil {
	//		return nil, err
	//	}
	//}
	//
	//tag, err := factory.MetaRepo.Add(&tagObj)
	//if err != nil {
	//	return nil, err
	//}
	//tagE := tag.(*entity.Tag)
	//var tagUsrObj entity.TagUser
	//tagUsrObj.ID = tagE.Id
	//tagUsrObj.UserID = user
	//_, err = factory.MetaRepo.Add(&tagUsrObj)
	//if err != nil {
	//	return nil, err
	//}

	// tagInfo := (tag.ShowInfo()).(vo.TagDetailInfo)
	// tagGrpInfo := (tagGrp.ShowInfo()).(vo.TagGroupRes)

	// res := vo.TagInfoRes{
	// 	TagID:            tagInfo.ID,
	// 	TagValue:         tagInfo.Value,
	// 	TagDescribe:      tagInfo.Describe,
	// 	TagGroupID:       tagGrpInfo.TagGroupID,
	// 	TagGroupValue:    tagGrpInfo.TagGroupValue,
	// 	TagGroupDescribe: tagGrpInfo.TagGroupDescribe,
	// }
	return &detail, nil
}

func (factory *TagFactory) UpdateTag(auth infra.AuthInfo, req vo.UpdateTagReq) (*entity.Tag, error) {
	var obj entity.Tag
	if err := factory.TagMetaRepo.FindOne(&obj, map[string]interface{}{
		"id": req.Id,
	}); err != nil {
		return nil, err
	}

	obj.Value = req.Value
	obj.Describe = req.Describe
	obj.GroupID = req.GroupID

	if err := factory.TagMetaRepo.FindOne(&entity.TagGroup{}, map[string]interface{}{
		"id": obj.GroupID,
	}); err != nil {
		return nil, err
	}

	if err := factory.TagMetaRepo.UptTag(&obj, auth); err != nil {
		return nil, err
	}
	return nil, nil

	//tagIDNum, _ := strconv.ParseInt(req.ID, 10, 64)
	//tagObj.Id = tagIDNum
	//tagObj.Value = req.Value
	//tagObj.Describe = req.Describe
	//tagGrpNum, _ := strconv.ParseInt(req.GroupID, 10, 64)
	//tagObj.GroupID = tagGrpNum
	//
	//tag, err := factory.MetaRepo.Upt(&tagObj)
	//if err != nil {
	//	return nil, err
	//}
	// var obj entity.Tags
	// var query = map[string]interface{}{}
	// query["id"] = tagObj.Id
	// if err := factory.MetaRepo.FindOne(&obj, query); err != nil {
	// 	return entity.Tags{}, err
	// }
	//return obj, nil
}

func (factory *TagFactory) CreateTagGroup(auth infra.AuthInfo, req vo.CreateTagGroupReq) (*entity.TagGroup, error) {
	var obj entity.TagGroup
	obj.Value = req.Value
	obj.Describe = req.Describe

	//var taggds []entity.TagGroup
	//if err := factory.TagMetaRepo.FindBy(&taggds, map[string]interface{}{
	//	"value": req.Value,
	//}); err != nil {
	//	return nil, err
	//}
	//if len(taggds) > 0 {
	//	return nil, fmt.Errorf("create tag group failed, tag group value is exist")
	//}

	if err := factory.TagMetaRepo.AddGroup(&obj, auth); err != nil {
		return nil, err
	}
	return &obj, nil
}

//
//func (factory *TagFactory) CreateTagGroupUser(req vo.CreateTagGroupUserReq) (infra.Entity, error) {
//	var tagGrpUsrObj entity.TagGroupUser
//	tagGrpUsrObj.ID = req.ID
//	tagGrpUsrObj.UserID = req.UserID
//
//	return factory.MetaRepo.Add(&tagGrpUsrObj)
//}

func (factory *TagFactory) UpdateTagGroup(auth infra.AuthInfo, req vo.UpdateTagGroupReq) (*entity.TagGroup, error) {
	var obj entity.TagGroup
	if err := factory.TagMetaRepo.FindOne(&obj, map[string]interface{}{
		"id": req.GroupID,
	}); err != nil {
		return nil, err
	}
	obj.Value = req.GroupValue
	obj.Describe = req.GroupDescribe
	if err := factory.TagMetaRepo.UptGroup(&obj, auth); err != nil {
		return nil, err
	}
	return &obj, nil

	//var tagGrpObj entity.TagGroup
	//groupIDNum, _ := strconv.ParseInt(req.GroupID, 10, 64)
	//tagGrpObj.Id = groupIDNum
	//tagGrpObj.Value = req.GroupValue
	//tagGrpObj.Describe = req.GroupDescribe
	//
	//tagGrp, err := factory.MetaRepo.Upt(&tagGrpObj)
	//if err != nil {
	//	return nil, err
	//}
	//// var obj entity.TagGroupSnap
	//// var query = map[string]interface{}{}
	//// query["id"] = tagGrpObj.Id
	//// if err := factory.MetaRepo.FindOne(&obj, query); err != nil {
	//// 	return entity.TagGroupSnap{}, err
	//// }
	//return tagGrp, nil
}

func (factory *TagFactory) DelTag(auth infra.AuthInfo, id int64) (*entity.Tag, error) {
	var obj entity.Tag
	obj.Id = id
	if err := factory.TagMetaRepo.DelTag(&obj, auth); err != nil {
		return nil, err
	}

	return &obj, nil
}

func (factory *TagFactory) DelTagGroup(auth infra.AuthInfo, id int64) (*entity.TagGroup, error) {
	var obj entity.TagGroup
	obj.Id = id
	if err := factory.TagMetaRepo.DelGroup(&obj, auth); err != nil {
		return nil, err
	}

	return &obj, nil
}

func (factory *TagFactory) MarkTag(req vo.TagObj) (*entity.TagObj, error) {
	// check if it is exist
	switch req.ObjType {
	case infra.Config:
		if err := factory.TagMetaRepo.FindOne(&entity.Config{}, map[string]interface{}{
			"id": req.ObjID,
		}); err != nil {
			return nil, err
		}
	case infra.Instance:
		if err := factory.TagMetaRepo.FindOne(&entity.Instance{}, map[string]interface{}{
			"id": req.ObjID,
		}); err != nil {
			return nil, err
		}
	default:
		return nil, infra.UnKnownKind
	}

	var tag entity.TagObj
	tag.TagId = req.TagID
	tag.ObjId = req.ObjID
	tag.ObjType = req.ObjType

	if _, err := factory.TagMetaRepo.Add(&tag); err != nil {
		return nil, err
	}
	return &tag, nil
}

func (factory *TagFactory) UnMarkTag(req vo.TagObj) (infra.Entity, error) {
	var obj entity.TagObj
	req.TagID = req.TagID
	req.ObjID = req.ObjID
	req.ObjType = req.ObjType

	//var obj entity.TagObj
	//tagObj.TagID = req.TagID
	//tagObj.ObjID = req.ObjID
	//tagObj.ObjType = req.ObjType
	//tagObj.Deleted = time.Now()
	if _, err := factory.MetaRepo.Del(&obj); err != nil {
		return nil, err
	}
	return nil, nil
}

func (factory *TagFactory) FindTagObjs(auth infra.AuthInfo, val string) (interface{}, error) {
	var tag []entity.Tag
	if err := factory.TagMetaRepo.FindTag(&tag, auth, map[string]interface{}{
		"value": val,
	}); err != nil {
		return nil, err
	}
	if len(tag) == 0 {
		return nil, infra.DataNotFind
	}

	var tagToObjsT []entity.TagObj
	for _, v := range tag {
		var tagToObjs []entity.TagObj
		if err := factory.TagMetaRepo.FindBy(&tagToObjs, map[string]interface{}{
			"tag_id": v.Id,
		}); err != nil {
			return nil, fmt.Errorf("here %v", err)
		}
		tagToObjsT = append(tagToObjsT, tagToObjs...)
	}

	var cfgsT []entity.Config
	var itasT []entity.Instance

	for _, v := range tagToObjsT {
		switch v.ObjType {
		case infra.Config:
			var cfgs []entity.Config
			if err := factory.TagMetaRepo.FindObjs(&cfgs, map[string]interface{}{
				"tag_id":   v.TagId,
				"obj_type": v.ObjType,
			}); err != nil {
				return nil, err
			}
			cfgsT = append(cfgsT, cfgs...)
		case infra.Instance:
			var itas []entity.Instance
			if err := factory.TagMetaRepo.FindObjs(&itas, map[string]interface{}{
				"tag_id":   v.TagId,
				"obj_type": v.ObjType,
			}); err != nil {
				return nil, err
			}
			itasT = append(itasT, itas...)
		}
	}

	return map[string]interface{}{
		"total_count":    len(cfgsT) + len(itasT),
		"config_count":   len(cfgsT),
		"config":         cfgsT,
		"instance_count": len(itasT),
		"instance":       itasT,
	}, nil
}

func (factory *TagFactory) FindAllTagObjs(auth infra.AuthInfo) (interface{}, error) {
	var tag []entity.TagDetail
	if err := factory.TagMetaRepo.FindTagDetail(&tag, auth, map[string]interface{}{}); err != nil {
		return nil, err
	}
	if len(tag) == 0 {
		return nil, infra.DataNotFind
	}

	var res = make([]interface{}, len(tag))
	for k, v := range tag {
		var obj = map[string]interface{}{
			"tag_id":             v.Id,
			"tag_value":          v.Value,
			"tag_describe":       v.Describe,
			"tag_group_id":       v.GroupID,
			"tag_group_value":    v.GroupID,
			"tag_group_describe": v.GroupDescribe,
		}

		var cfgs []entity.Config
		if err := factory.TagMetaRepo.FindObjs(&cfgs, map[string]interface{}{
			"tag_id":   v.Id,
			"obj_type": infra.Config,
		}); err != nil {
			return nil, err
		}

		var itas []entity.Instance
		if err := factory.TagMetaRepo.FindObjs(&itas, map[string]interface{}{
			"tag_id":   v.Id,
			"obj_type": infra.Instance,
		}); err != nil {
			return nil, err
		}

		obj["tag_objs"] = map[string]interface{}{
			"total_count":    len(cfgs) + len(itas),
			"config_count":   len(cfgs),
			"config":         cfgs,
			"instance_count": len(itas),
			"instance":       itas,
		}

		res[k] = obj
	}

	return res, nil
}
